package com.vvv.designpattern.service;

import com.vvv.designpattern.constants.RedisConstants;
import com.vvv.designpattern.ordermanagement.command.command.OrderCommand;
import com.vvv.designpattern.ordermanagement.command.invoker.OrderCommandInvoker;
import com.vvv.designpattern.ordermanagement.state.OrderState;
import com.vvv.designpattern.ordermanagement.state.OrderStateChangeAction;
import com.vvv.designpattern.pay.facade.PayFacade;
import com.vvv.designpattern.pojo.Order;
import com.vvv.designpattern.service.inter.OrderServiceInterface;
import com.vvv.designpattern.transaction.colleague.AbstractCustomer;
import com.vvv.designpattern.transaction.colleague.Buyer;
import com.vvv.designpattern.transaction.colleague.Payer;
import com.vvv.designpattern.transaction.mediator.Mediator;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.persist.StateMachinePersister;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author vvv
 * @date 2023-11-21 13 29
 * discription
 * Q：订单状态和状态机为什么要用redis进行存储？
 * A：因为在分布式系统中，不同的订单状态可能要由不同的服务进行处理
 */
@Service
public class OrderService implements OrderServiceInterface {
    /**
     * spring 状态机
     */
    @Resource
    private StateMachine<OrderState, OrderStateChangeAction> orderStateMachine;

    /**
     * 操作redis中的spring状态机
     */
    @Resource
    private StateMachinePersister<OrderState, OrderStateChangeAction, String> stateMachineRedisPersister;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private OrderCommand orderCommand;

    @Resource
    private PayFacade payFacade;

    @Resource
    private Mediator mediator;

    /**
     * createOrder中不需要想状态机发event，因为状态机start后的状态就是去配置文件中读取初始化状态的
     * @param productId
     * @return
     */
    @Override
    public Order createOrder(String productId) {
        // 1. 生成订单的全局id
        String orderId = "OID" + productId;
        // 2. 生成order对象
        Order order = Order.builder()
                .orderId(orderId)
                .productId(productId)
                .orderState(OrderState.ORDER_WAIT_PAY)
                .build();
        // 3. 将Order对象存入redis设置15分钟过期
        String key = RedisConstants.ORDER_STATE_KEY_PREFIX + orderId;
        redisTemplate.opsForValue().set(key, order, 900, TimeUnit.SECONDS);
        // 4. 命令模式，调用创建订单后的后序逻辑
        OrderCommandInvoker orderCommandInvoker = new OrderCommandInvoker();
        orderCommandInvoker.invoke(orderCommand, order);
        return order;
    }

    @Override
    public Order pay(String orderId) {
        // 1. 从redis中获取订单信息
        String key = RedisConstants.ORDER_STATE_KEY_PREFIX + orderId;
        Order order = (Order) redisTemplate.opsForValue().get(key);
        // 2. 封装成Message
        Message<OrderStateChangeAction> message = MessageBuilder
                .withPayload(OrderStateChangeAction.PAY_ORDER)
                .setHeader("order", order)
                .build();
        // 3. 发消息给spring状态机
        if (this.changeStateAction(message, order)) {
            return order;
        }
        return null;
    }

    @Override
    public Order send(String orderId) {
        // TODO ... 业务处理


        // 1. 从redis中获取订单信息
        String key = RedisConstants.ORDER_STATE_KEY_PREFIX + orderId;
        Order order = (Order) redisTemplate.opsForValue().get(key);
        // 2. 封装成Message
        Message<OrderStateChangeAction> message = MessageBuilder
                .withPayload(OrderStateChangeAction.SEND_ORDER)
                .setHeader("order", order)
                .build();
        // 3. 发消息给spring状态机
        if (this.changeStateAction(message, order)) {
            return order;
        }
        return null;
    }

    @Override
    public Order receive(String orderId) {
        // TODO ... 业务处理


        // 1. 从redis中获取订单信息
        String key = RedisConstants.ORDER_STATE_KEY_PREFIX + orderId;
        Order order = (Order) redisTemplate.opsForValue().get(key);
        // 2. 封装成Message
        Message<OrderStateChangeAction> message = MessageBuilder
                .withPayload(OrderStateChangeAction.RECEIVE_ORDER)
                .setHeader("order", order)
                .build();
        // 3. 发消息给spring状态机
        if (this.changeStateAction(message, order)) {
            return order;
        }
        return null;
    }

    @Override
    public String getPayUrl(String orderId, Float price, Integer payType) {
        // 1. 从redis中获取订单信息
        String key = RedisConstants.ORDER_STATE_KEY_PREFIX + orderId;
        Order order = (Order) redisTemplate.opsForValue().get(key);
        if (order == null) {
            throw new UnsupportedOperationException("the order is not exists !");
        }
        order.setPrice(price);
        // 2. 调用门面模式的pay方法
        /**
         * 我们可以看到调用方无需关心具体的策略类，直接通过门面类调用支付即可
         */
        return payFacade.pay(order, payType);
    }

    /**
     * 给spring状态机发event
     * @param message
     * @param order
     * @return
     */
    private boolean changeStateAction(Message<OrderStateChangeAction> message, Order order) {
        try {
            // 1. 启动状态机，这时状态机的状态为配置文件中的初始状态
            orderStateMachine.start();
            // 2. 从redis中读取状态机，第一次调用redis中是没有数据的，也就是pay方法
            String stateMachineKey = RedisConstants.STATE_MACHINE_KEY_PREFIX + order.getOrderId();
            stateMachineRedisPersister.restore(orderStateMachine, stateMachineKey);
            // 3. 发event给这个状态机
            boolean res = orderStateMachine.sendEvent(message);
            // 4. 将更改完状态的状态机 重新缓存入redis
            if (res && order.getOrderState() != OrderState.ORDER_FINISH) {
                // 如果sendEvent状态转换失败了，或者receive操作成功之后了，就不需要再去保存状态机信息到redis中了
                stateMachineRedisPersister.persist(orderStateMachine, stateMachineKey);
            }
            return res;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            orderStateMachine.stop();
        }
        return false;
    }


    public void friendPay(String sourceCustomer, String orderId, String targetCustomer, String payResult, String role) {
        Buyer buyer = new Buyer(mediator, orderId, sourceCustomer);
        Payer payer = new Payer(mediator, orderId, targetCustomer);
        Map<String, AbstractCustomer> map = new HashMap<>();
        map.put("buyer", buyer);
        map.put("payer", payer);
        mediator.customerInstances.put(orderId, map);
        if (role.equals("B")) {
            buyer.messageTransfer(orderId, targetCustomer, payResult);
        } else {
            payer.messageTransfer(orderId, targetCustomer, payResult);
        }
    }
}
